Skip to main content

Q-3: Secondary Storage Permissions OR Telephony API (5 Marks)

Questions​

a) What necessary permissions are required to store data on secondary memory in android? Discuss various methods associated to store data on secondary memory. (5 marks)

OR

b) Write a note on telephony API and its key features. (5 marks)


Answers​

a) Secondary Storage Permissions and Methods​

Required Permissions for Secondary Storage​

1. Manifest Permissions (Before Android 10):

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

2. Runtime Permissions (Android 6.0+):

// Check and request permissions
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {

ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE},
REQUEST_CODE_STORAGE_PERMISSION);
}

3. Scoped Storage (Android 10+):

  • Limited external storage access
  • App-specific directories don't require permissions
  • MediaStore API for media files
  • Storage Access Framework for document access

Permission Handling Code​

public class StoragePermissionHelper {
private static final int REQUEST_CODE_STORAGE = 100;
private Activity activity;

public StoragePermissionHelper(Activity activity) {
this.activity = activity;
}

public boolean hasStoragePermission() {
return ContextCompat.checkSelfPermission(activity,
Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
}

public void requestStoragePermission() {
ActivityCompat.requestPermissions(activity,
new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
}, REQUEST_CODE_STORAGE);
}

public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_CODE_STORAGE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted
Toast.makeText(activity, "Storage permission granted", Toast.LENGTH_SHORT).show();
} else {
// Permission denied
Toast.makeText(activity, "Storage permission denied", Toast.LENGTH_SHORT).show();
}
}
}
}

Methods to Store Data on Secondary Memory​

1. External Storage Directories

public class ExternalStorageHelper {

// Get external storage directory
public File getExternalStorageDir() {
return Environment.getExternalStorageDirectory();
}

// Get app-specific external directory (No permission required Android 4.4+)
public File getAppExternalDir(Context context) {
return context.getExternalFilesDir(null);
}

// Get external cache directory
public File getExternalCacheDir(Context context) {
return context.getExternalCacheDir();
}

// Check if external storage is available
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(state);
}

// Check if external storage is readable
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state);
}
}

2. File Writing to External Storage

public void writeToExternalStorage(String filename, String data) {
if (isExternalStorageWritable()) {
File file = new File(getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), filename);

try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(data.getBytes());
fos.close();
Toast.makeText(this, "File saved to external storage", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "Error saving file", Toast.LENGTH_SHORT).show();
}
}
}

// Read from external storage
public String readFromExternalStorage(String filename) {
if (isExternalStorageReadable()) {
File file = new File(getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), filename);

try {
FileInputStream fis = new FileInputStream(file);
byte[] buffer = new byte[(int) file.length()];
fis.read(buffer);
fis.close();
return new String(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}

3. MediaStore API (For Media Files)

public void saveImageToMediaStore(Bitmap bitmap, String displayName) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, displayName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
}

Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

try {
OutputStream outputStream = getContentResolver().openOutputStream(uri);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.close();
Toast.makeText(this, "Image saved to gallery", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}

4. Storage Access Framework (SAF)

// Open document picker
private void openDocumentPicker() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/*");
startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT);
}

// Create document
private void createDocument() {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TITLE, "newfile.txt");
startActivityForResult(intent, REQUEST_CODE_CREATE_DOCUMENT);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_CREATE_DOCUMENT && resultCode == RESULT_OK) {
Uri uri = data.getData();
writeToUri(uri, "Hello World!");
}
}

private void writeToUri(Uri uri, String content) {
try {
OutputStream outputStream = getContentResolver().openOutputStream(uri);
outputStream.write(content.getBytes());
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}

b) Telephony API and Key Features​

Definition​

Telephony API in Android provides access to telephony-related information and functionality, allowing applications to interact with the device's phone capabilities.

Key Classes​

  • TelephonyManager: Main class for telephony operations
  • SmsManager: Handle SMS operations
  • PhoneStateListener: Monitor phone state changes
  • CallLog: Access call history

Required Permissions​

<!-- Basic telephony permissions -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />

<!-- SMS permissions -->
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />

<!-- Call permissions -->
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />

<!-- Location for cell info -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

Key Features and Implementation​

1. Phone Information

public class TelephonyHelper {
private TelephonyManager telephonyManager;
private Context context;

public TelephonyHelper(Context context) {
this.context = context;
telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
}

// Get device information
@SuppressLint("MissingPermission")
public void getDeviceInfo() {
// Network operator name
String operatorName = telephonyManager.getNetworkOperatorName();

// Device ID (IMEI)
String deviceId = telephonyManager.getDeviceId();

// Phone number
String phoneNumber = telephonyManager.getLine1Number();

// Network type
int networkType = telephonyManager.getNetworkType();
String networkTypeName = getNetworkTypeName(networkType);

// SIM state
int simState = telephonyManager.getSimState();
String simStateName = getSimStateName(simState);

Log.d("TelephonyInfo", "Operator: " + operatorName);
Log.d("TelephonyInfo", "Device ID: " + deviceId);
Log.d("TelephonyInfo", "Phone Number: " + phoneNumber);
Log.d("TelephonyInfo", "Network Type: " + networkTypeName);
Log.d("TelephonyInfo", "SIM State: " + simStateName);
}

private String getNetworkTypeName(int networkType) {
switch (networkType) {
case TelephonyManager.NETWORK_TYPE_GPRS: return "GPRS";
case TelephonyManager.NETWORK_TYPE_EDGE: return "EDGE";
case TelephonyManager.NETWORK_TYPE_UMTS: return "UMTS";
case TelephonyManager.NETWORK_TYPE_HSDPA: return "HSDPA";
case TelephonyManager.NETWORK_TYPE_LTE: return "LTE";
default: return "Unknown";
}
}

private String getSimStateName(int simState) {
switch (simState) {
case TelephonyManager.SIM_STATE_ABSENT: return "No SIM";
case TelephonyManager.SIM_STATE_READY: return "SIM Ready";
case TelephonyManager.SIM_STATE_LOCKED: return "SIM Locked";
default: return "Unknown";
}
}
}

2. Phone State Monitoring

public class PhoneStateMonitor extends PhoneStateListener {
private Context context;

public PhoneStateMonitor(Context context) {
this.context = context;
}

@Override
public void onCallStateChanged(int state, String phoneNumber) {
super.onCallStateChanged(state, phoneNumber);

switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
Log.d("PhoneState", "Phone is idle");
break;
case TelephonyManager.CALL_STATE_RINGING:
Log.d("PhoneState", "Phone is ringing: " + phoneNumber);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Log.d("PhoneState", "Phone is in call");
break;
}
}

@Override
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
super.onSignalStrengthsChanged(signalStrength);
Log.d("PhoneState", "Signal strength changed: " + signalStrength.toString());
}

@Override
public void onDataConnectionStateChanged(int state, int networkType) {
super.onDataConnectionStateChanged(state, networkType);

switch (state) {
case TelephonyManager.DATA_CONNECTED:
Log.d("PhoneState", "Data connected");
break;
case TelephonyManager.DATA_DISCONNECTED:
Log.d("PhoneState", "Data disconnected");
break;
}
}
}

// Register listener in activity
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
PhoneStateMonitor phoneStateMonitor = new PhoneStateMonitor(this);
telephonyManager.listen(phoneStateMonitor, PhoneStateListener.LISTEN_CALL_STATE |
PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);

3. SMS Operations

public class SMSHelper {

// Send SMS
public void sendSMS(String phoneNumber, String message) {
try {
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNumber, null, message, null, null);
Toast.makeText(context, "SMS sent successfully", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(context, "SMS failed to send", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}

// Send long SMS (multipart)
public void sendLongSMS(String phoneNumber, String message) {
SmsManager smsManager = SmsManager.getDefault();
ArrayList<String> parts = smsManager.divideMessage(message);
smsManager.sendMultipartTextMessage(phoneNumber, null, parts, null, null);
}

// Read SMS from inbox
@SuppressLint("Range")
public List<SMS> readSMS() {
List<SMS> smsList = new ArrayList<>();
Uri smsUri = Uri.parse("content://sms/inbox");

Cursor cursor = context.getContentResolver().query(smsUri, null, null, null, null);

if (cursor != null && cursor.moveToFirst()) {
do {
String address = cursor.getString(cursor.getColumnIndex("address"));
String body = cursor.getString(cursor.getColumnIndex("body"));
long date = cursor.getLong(cursor.getColumnIndex("date"));

SMS sms = new SMS(address, body, date);
smsList.add(sms);
} while (cursor.moveToNext());

cursor.close();
}

return smsList;
}
}

4. Call Operations

public class CallHelper {

// Make phone call
public void makeCall(Context context, String phoneNumber) {
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:" + phoneNumber));

if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CALL_PHONE)
== PackageManager.PERMISSION_GRANTED) {
context.startActivity(callIntent);
}
}

// Open dialer
public void openDialer(Context context, String phoneNumber) {
Intent dialIntent = new Intent(Intent.ACTION_DIAL);
dialIntent.setData(Uri.parse("tel:" + phoneNumber));
context.startActivity(dialIntent);
}

// Read call log
@SuppressLint("Range")
public List<CallLogEntry> readCallLog(Context context) {
List<CallLogEntry> callLog = new ArrayList<>();

Cursor cursor = context.getContentResolver().query(
CallLog.Calls.CONTENT_URI, null, null, null,
CallLog.Calls.DATE + " DESC");

if (cursor != null && cursor.moveToFirst()) {
do {
String number = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER));
String name = cursor.getString(cursor.getColumnIndex(CallLog.Calls.CACHED_NAME));
int type = cursor.getInt(cursor.getColumnIndex(CallLog.Calls.TYPE));
long date = cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DATE));
int duration = cursor.getInt(cursor.getColumnIndex(CallLog.Calls.DURATION));

CallLogEntry entry = new CallLogEntry(number, name, type, date, duration);
callLog.add(entry);
} while (cursor.moveToNext());

cursor.close();
}

return callLog;
}
}

Applications of Telephony API​

  • Call Management Apps: Call blocking, call recording
  • SMS Applications: Custom SMS clients, SMS backup
  • Network Monitoring: Signal strength monitoring, network analysis
  • Security Apps: Device tracking, anti-theft features
  • Communication Apps: VoIP applications, messaging platforms
  • Business Apps: Customer relationship management, sales tracking